<?php 

namespace pmvc\ioc\annotation;

use pmvc\util\StringUtil;
use pmvc\util\FileUtil;
use pmvc\cache\LocalCacheImpl;
use pmvc\cache\CacheUtil;
use pmvc\cache\Cache;
use pmvc\ioc\InitializingObject;

use Exception;

/**
 * An {@link AutowireCandidateProvider} that reads
 * candidates from a file.
 *
 */
class FileAutowireCandidateProvider
	implements AutowireCandidateProvider,
	InitializingObject {

	/**
	 * @var string
	 */
	private $fileName;

	/**
	 * @var string
	 */
	private $relativeFileName;

	/**
	 * @var Cache
	 */
	private $cache;

	/**
	 * @var int
	 */
	private $ttl = Cache::ONE_HOUR;

	/**
	 * @var bool
	 */
	private $useCache = true;
	
	/**
	 * {@inheritDoc}
	 */
	public function initialize() {
		if ($this->useCache && !isset($this->cache)) {
			$this->cache = new LocalCacheImpl();
		}
	}
	
	/**
	 * {@inheritDoc}
	 */
	public function getAutowireCandidates() {

		// bail early
		if (!$this->useCache) {
			return $this->readCandidates();
		}

		// get the file name
		$fileName = $this->getFileName();

		// generate cache key
		$cacheKey = __CLASS__."_".md5(realpath($fileName));

		// the value to return
		$val = CacheUtil::statGet($fileName, $this->cache, $cacheKey, null);

		// update the value
		if (!isset($val)) {
			$val = $this->readCandidates();
			CacheUtil::statSet($fileName, $this->cache, $cacheKey, $val);
		}

		// return it
		return $val;
	}

	/**
	 * Reads candidate class names from the given file.
	 * Each classname must be on it's own line.  A line
	 * starting with {@code #} is considered a comment
	 * and will be discarded.
	 * 
	 * @return array an array of candidate class names
	 * @throws Exception on error
	 */
	private function readCandidates() {

		// get file name
		$fileName = $this->getFileName();
		if ($fileName===false || !file_exists($fileName)) {
			throw new Exception("Error opening file: ".$this->fileName);
		}

		// read and return
		$ret = Array();
		foreach (file($fileName) as $line) {
			$line = trim($line);
			if (strlen($line)!=0 && !StringUtil::startsWith($line, "#")) {
				$ret []= $line;
			}
		}
		return $ret;
	}

	/**
	 * Returns the file name or false if it's not found.
	 * @return string
	 */
	private function getFileName() {
		return isset($this->relativeFileName)
			? FileUtil::getRelativePath($this->fileName, $this->relativeFileName)
			: $this->fileName;
	}

	public function setFileName($fileName) {
		$this->fileName = $fileName;
	}

	public function setRelativeFileName($relativeFileName) {
		$this->relativeFileName = $relativeFileName;
	}

	public function setCache(Cache $cache) {
		$this->cache = $cache;
	}

	public function setUseCache($useCache) {
		$this->useCache = $useCache;
	}

	public function setTTL($ttl) {
		$this->ttl = $ttl;
	}

}

?>